﻿using AF_Application.Interface.Repertory;
using AF_Application.Interface.SortExpression;
using AF_Application.Interface.Specifications;
using AF_DataModel;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace AF_Repertory.Repertories
{
    public class Repertory : IDBRepertory
    {
        private DBContainerWithCodeFirst _DB = null;
        public DBContainerWithCodeFirst DB
        {
            get
            {
                if (_DB == null)
                {
                    _DB = new DBContainerWithCodeFirst();
                }
                return _DB;
            }
        }



        public virtual T GetOne<T>(ISpecification<T> condition, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            if (queryOption == EnumQueryOption.实时)
            {
                return DB.Set<T>().AsNoTracking().FirstOrDefault(condition.GetExpression());
            }
            else
            {
                return DB.Set<T>().FirstOrDefault(condition.GetExpression());
            }
        }
        public virtual T GetOne<T>(ISpecification<T> condition, Expression<Func<T, dynamic>> order, EnumSortType orderType, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            if (orderType == EnumSortType.正序)
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    return DB.Set<T>().AsNoTracking().SortBy(order).FirstOrDefault(condition.GetExpression());
                }
                else
                {
                    return DB.Set<T>().SortBy(order).FirstOrDefault(condition.GetExpression());
                }
            }
            else
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    return DB.Set<T>().AsNoTracking().SortByDescending(order).FirstOrDefault(condition.GetExpression());
                }
                else
                {
                    return DB.Set<T>().SortByDescending(order).FirstOrDefault(condition.GetExpression());
                }
            }
        }

        public virtual T GetOne<T>(ISpecification<T> condition, string order, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class, IDataModel
        {
            if (queryOption == EnumQueryOption.实时)
            {
                if (!string.IsNullOrEmpty(order))
                {
                    return SortExpressionQuery.Query(DB.Set<T>().AsNoTracking(), order).FirstOrDefault(condition.GetExpression());
                }
                else
                {
                    return DB.Set<T>().AsNoTracking().FirstOrDefault(condition.GetExpression());
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(order))
                {
                    return SortExpressionQuery.Query(DB.Set<T>(), order).FirstOrDefault(condition.GetExpression());
                }
                else
                {
                    return DB.Set<T>().FirstOrDefault(condition.GetExpression());
                }
            }
        }
        public virtual IQueryable<T> Find<T>(ISpecification<T> condition, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            if (queryOption == EnumQueryOption.实时)
            {
                return DB.Set<T>().AsNoTracking().Where(condition.GetExpression());
            }
            else
            {
                return DB.Set<T>().Where(condition.GetExpression());
            }
        }

        public virtual IQueryable<T> Find<T>(ISpecification<T> condition, Expression<Func<T, dynamic>> order, EnumSortType orderType, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            if (orderType == EnumSortType.正序)
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    return DB.Set<T>().AsNoTracking().Where(condition.GetExpression()).SortBy(order);
                }
                else
                {
                    return DB.Set<T>().Where(condition.GetExpression()).SortBy(order);
                }
            }
            else
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    return DB.Set<T>().AsNoTracking().Where(condition.GetExpression()).SortByDescending(order);
                }
                else
                {
                    return DB.Set<T>().Where(condition.GetExpression()).SortByDescending(order);
                }
            }
        }


        public virtual IQueryable<T> Find<T>(ISpecification<T> condition, string order, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class, IDataModel
        {
            if (queryOption == EnumQueryOption.实时)
            {
                if (!string.IsNullOrEmpty(order))
                {
                    return SortExpressionQuery.Query(DB.Set<T>().AsNoTracking().Where(condition.GetExpression()), order);
                }
                else
                {
                    return DB.Set<T>().AsNoTracking().Where(condition.GetExpression());
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(order))
                {
                    return SortExpressionQuery.Query(DB.Set<T>().Where(condition.GetExpression()), order);
                }
                else
                {
                    return DB.Set<T>().Where(condition.GetExpression());
                }
            }
        }

        public virtual PagedResult<T> Find<T>(ISpecification<T> condition, Expression<Func<T, dynamic>> order, EnumSortType orderType, int pageIndex, int pageSize, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            PagedResult<T> result = new PagedResult<T>()
            {
                AllCount = Count<T>(condition),
                PageIndex = pageIndex,
                PageSize = pageSize
            };
            result.PageCount = (int)Math.Ceiling((double)result.AllCount / (double)result.PageSize);

            if (orderType == EnumSortType.正序)
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    result.Data = DB.Set<T>().AsNoTracking().Where(condition.GetExpression()).SortBy(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().Where(condition.GetExpression()).SortBy(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            else
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    result.Data = DB.Set<T>().AsNoTracking().Where(condition.GetExpression()).SortByDescending(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().Where(condition.GetExpression()).SortByDescending(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            return result;
        }
        public virtual PagedResult<T> Find<T>(ISpecification<T> condition, string order, int pageIndex, int pageSize, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class, IDataModel
        {
            PagedResult<T> result = new PagedResult<T>()
            {
                AllCount = Count<T>(condition),
                PageIndex = pageIndex,
                PageSize = pageSize
            };
            result.PageCount = (int)Math.Ceiling((double)result.AllCount / (double)result.PageSize);
            if (queryOption == EnumQueryOption.实时)
            {
                if (!string.IsNullOrEmpty(order))
                {
                    result.Data = SortExpressionQuery.Query(DB.Set<T>().AsNoTracking().Where(condition.GetExpression()), order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().AsNoTracking().Where(condition.GetExpression()).OrderBy(p => p.Id).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(order))
                {
                    result.Data = SortExpressionQuery.Query(DB.Set<T>().Where(condition.GetExpression()), order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().Where(condition.GetExpression()).OrderBy(p => p.Id).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            return result;
        }
        public virtual bool Update<T>(T entity, bool saveNow = true) where T : class,IDataModel
        {
            try
            {
                DB.Set<T>().Attach(entity);
                DB.Entry<T>(entity).State = EntityState.Modified;
                return saveNow ? DB.SaveChanges() > 0 : true;
            }
            catch (DbEntityValidationException exp)
            {
                throw new Exception(GetDBError(exp));
            }
            catch (InvalidOperationException exp)
            {
                if (entity != null)
                {
                    T oldEntity = GetByID<T>(entity.Id, EnumQueryOption.常规);
                    DB.Entry(oldEntity).CurrentValues.SetValues(entity);
                    return DB.SaveChanges() > 0;
                }
                else
                {
                    return false;
                }
            }
        }

        public virtual bool Add<T>(T entity, bool saveNow = true) where T : class,IDataModel
        {
            try
            {
                DB.Entry<T>(entity).State = EntityState.Added;
                return saveNow ? DB.SaveChanges() > 0 : true;
            }
            catch (DbEntityValidationException exp)
            {
                throw new Exception(GetDBError(exp));
            }
        }

        public virtual bool Delete<T>(T entity, bool saveNow = true) where T : class,IDataModel
        {
            try
            {
                DB.Set<T>().Attach(entity);
                DB.Entry<T>(entity).State = EntityState.Deleted;
                return saveNow ? DB.SaveChanges() > 0 : true;
            }
            catch (DbEntityValidationException exp)
            {
                throw new Exception(GetDBError(exp));
            }
        }

        private string GetDBError(DbEntityValidationException exp)
        {
            StringBuilder SB = new StringBuilder();
            if (exp.EntityValidationErrors != null)
            {
                foreach (var E in exp.EntityValidationErrors)
                {
                    foreach (var I in E.ValidationErrors)
                    {
                        SB.Append(I.ErrorMessage + ";");
                    }
                }
            }
            return SB.ToString();
        }
        public virtual int Count<T>(ISpecification<T> condition) where T : class,IDataModel
        {
            return DB.Set<T>().AsNoTracking().Count(condition.GetExpression());
        }

        public virtual int Count<T>() where T : class,IDataModel
        {
            return DB.Set<T>().AsNoTracking().Count();
        }


        public virtual IQueryable<T> GetAll<T>(EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            if (queryOption == EnumQueryOption.实时)
            {
                return DB.Set<T>().AsNoTracking();
            }
            else
            {
                return DB.Set<T>();
            }
        }

        public virtual IQueryable<T> GetAll<T>(Expression<Func<T, dynamic>> order, EnumSortType orderType, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            if (orderType == EnumSortType.正序)
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    return DB.Set<T>().AsNoTracking().SortBy(order);
                }
                else
                {
                    return DB.Set<T>().SortBy(order);
                }
            }
            else
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    return DB.Set<T>().AsNoTracking().SortByDescending(order);
                }
                else
                {
                    return DB.Set<T>().SortByDescending(order);
                }
            }
        }

        public virtual IQueryable<T> GetAll<T>(string order, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class, IDataModel
        {
            if (queryOption == EnumQueryOption.实时)
            {
                if (!string.IsNullOrEmpty(order))
                {
                    return SortExpressionQuery.Query(DB.Set<T>().AsNoTracking(), order);
                }
                else
                {
                    return DB.Set<T>().AsNoTracking();
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(order))
                {
                    return SortExpressionQuery.Query(DB.Set<T>(), order);
                }
                else
                {
                    return DB.Set<T>();
                }
            }
        }

        public virtual PagedResult<T> GetAll<T>(Expression<Func<T, dynamic>> order, EnumSortType orderType, int pageIndex, int pageSize, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class,IDataModel
        {
            PagedResult<T> result = new PagedResult<T>()
            {
                AllCount = Count<T>(),
                PageIndex = pageIndex,
                PageSize = pageSize
            };
            result.PageCount = (int)Math.Ceiling((double)result.AllCount / (double)result.PageSize);

            if (orderType == EnumSortType.正序)
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    result.Data = DB.Set<T>().AsNoTracking().SortBy(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().SortBy(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            else
            {
                if (queryOption == EnumQueryOption.实时)
                {
                    result.Data = DB.Set<T>().AsNoTracking().SortByDescending(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().SortByDescending(order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            return result;
        }

        public virtual PagedResult<T> GetAll<T>(string order, int pageIndex, int pageSize, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class, IDataModel
        {
            PagedResult<T> result = new PagedResult<T>()
            {
                AllCount = Count<T>(),
                PageIndex = pageIndex,
                PageSize = pageSize
            };
            result.PageCount = (int)Math.Ceiling((double)result.AllCount / (double)result.PageSize);

            if (queryOption == EnumQueryOption.实时)
            {
                if (!string.IsNullOrEmpty(order))
                {
                    result.Data = SortExpressionQuery.Query(DB.Set<T>().AsNoTracking(), order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().AsNoTracking().OrderBy(p => p.Id).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(order))
                {
                    result.Data = SortExpressionQuery.Query(DB.Set<T>(), order).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
                else
                {
                    result.Data = DB.Set<T>().OrderBy(p => p.Id).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                }
            }
            return result;
        }

        public virtual List<T> GetBySql<T>(string sql) where T : class,IDataModel
        {
            return DB.Database.SqlQuery<T>(sql).ToList();
        }


        public virtual int ExecuteSql(string sql)
        {
            return DB.Database.ExecuteSqlCommand(sql);
        }

        public virtual object ExecuteScalar(string sql)
        {
            object reuslt = null;
            using (MySqlConnection connection = new MySqlConnection(DB.Database.Connection.ConnectionString))
            {
                connection.Open();
                using (MySqlCommand command = new MySqlCommand(sql, connection))
                {
                    reuslt = command.ExecuteScalar();
                }
            }
            return reuslt;
        }


        public bool Delete<T>(Guid key, bool saveNow = true) where T : class, IDataModel
        {
            T toDel = GetByID<T>(key);
            if (toDel != null)
            {
                return Delete<T>(toDel, saveNow);
            }
            else
            {
                return false;
            }
        }


        public T GetByID<T>(Guid key, EnumQueryOption queryOption = EnumQueryOption.实时) where T : class, IDataModel
        {
            return GetOne<T>(Specification<T>.Eval(p => p.Id == key), queryOption);
        }


        public bool Add<T>(List<T> entityList, bool saveNow = true) where T : class, IDataModel
        {
            bool result = false;
            if (entityList != null)
            {
                foreach (var item in entityList)
                {
                    Add<T>(item, false);
                }
                result = saveNow ? DB.SaveChanges() > 0 : true;
            }
            return result;
        }

        public bool Update<T>(List<T> entityList, bool saveNow = true) where T : class, IDataModel
        {
            bool result = false;
            if (entityList != null)
            {
                foreach (var item in entityList)
                {
                    Update<T>(item, false);
                }
                result = saveNow ? DB.SaveChanges() > 0 : true;
            }
            return result;
        }

        public bool Delete<T>(List<T> entityList, bool saveNow = true) where T : class, IDataModel
        {
            bool result = false;
            if (entityList != null)
            {
                foreach (var item in entityList)
                {
                    Delete<T>(item, false);
                }
                result = saveNow ? DB.SaveChanges() > 0 : true;
            }
            return result;
        }

        public bool Delete<T>(List<Guid> keyList, bool saveNow = true) where T : class, IDataModel
        {
            bool result = false;
            if (keyList != null)
            {
                foreach (var item in keyList)
                {
                    Delete<T>(item, false);
                }
                result = saveNow ? DB.SaveChanges() > 0 : true;
            }
            return result;
        }

        public void Dispose()
        {
            DB.Dispose();
        }
    }
}
